home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / smaltalk / manchest.lha / MANCHESTER / usenet / st80_pre4 / 3state-buttons.st < prev    next >
Text File  |  1993-07-24  |  15KB  |  509 lines

  1. "    NAME        3state-buttons
  2.     AUTHOR        ross@prls.UUCP (Ross Morley)
  3.     FUNCTION a SwitchView with three states
  4.     ST-VERSIONS    2.3
  5.     PREREQUISITES     
  6.     CONFLICTS    
  7.     DISTRIBUTION      world
  8.     VERSION        1.1
  9.     DATE    31 Aug 90
  10. SUMMARY     A pluggable switch view with three states (black, grey and
  11. white) and two ways to press it (click or prolonged press).
  12. "
  13. !
  14. "
  15. Newsgroups: comp.lang.smalltalk
  16. Subject: tri-state button goody
  17. Keywords: goody goodies switch pluggable
  18. Message-ID: <43407@prls.UUCP>
  19. Date: 31 Aug 90 21:42:16 GMT
  20. Organization: Philips R&D Center, Sunnyvale, CA
  21.  
  22. Here's a little goody which many of you may find useful.
  23. It's a SwitchView with three states (black, grey and white) and two ways
  24. to press it (click or prolonged press). It can be plugged into your
  25. application very easily (see examples included) by defining three simple
  26. messages in your application class that is to be its model. You can name
  27. the messages as you like - simply tell the TriStateButtonView what they
  28. are called when you plug it in. The corresponding methods in your model
  29. class determine the semantics of the button: which color it is, and the
  30. effects of clicking or pressing it. I have included a contrived example
  31. application with two TriStateButtons with different semantics, so you can
  32. play with them as soon as you file this in. 
  33.  
  34. Ross P. Morley                                  pyramid!!prls!!ross
  35. Philips Research, Sunnyvale                     philabs!!prls!!ross
  36. 811 E. Arques Ave (MS02)            ross@prls.uucp
  37. Sunnyvale, CA 94088-3409                        Tel. (408) 991 5057
  38. "
  39.  
  40. 'From Smalltalk-80, Version 2.3 of 13 June 1988 on 31 August 1990 at 12:11:57 pm'!
  41.  
  42. SwitchView subclass: #TriStateButtonView
  43.     instanceVariableNames: ''
  44.     classVariableNames: ''
  45.     poolDictionaries: ''
  46.     category: 'Interface-VisualControls'!
  47.  
  48. TriStateButtonView comment:
  49. 'I am a pluggable tri-state button. I can display white, black or grey depending on the state of 
  50. my aspect in the model. My controller senses two kinds of events, a click and a prolonged press, 
  51. and signals the model accordingly. Some interfacing methods must be built into the model - see
  52. comments on instance creation methods.
  53.  
  54.                         Copyright (C) 1990, Ross P. Morley. 
  55.     This program is placed in the public domain. You may use and alter this program freely
  56.     for non-commercial purposes as long as you leave this message intact.  Neither I nor
  57.     my company will recognize any responsibility for damages arising from use of this program.
  58. '!
  59.  
  60. !TriStateButtonView methodsFor: 'controller access'!
  61.  
  62. defaultControllerClass
  63.     ^TriStateButtonController! !
  64.  
  65. !TriStateButtonView methodsFor: 'deEmphasizing'!
  66.  
  67. deEmphasizeView
  68.     "Redisplay without emphasis."
  69.  
  70.     emphasisOn _ false.
  71.     self displayView!
  72.  
  73. emphasizeView
  74.     "Redisplay with emphasis."
  75.  
  76.     emphasisOn _ true.
  77.     self displayView! !
  78.  
  79. !TriStateButtonView methodsFor: 'displaying'!
  80.  
  81. displayLabelClippingBox: aRectangle rule: ruleInteger mask: maskInteger
  82.     "Display the label in the specified manner."
  83.  
  84.     | center |
  85.     label isNil ifTrue: [^self].
  86.     center _ label boundingBox center.
  87.     label displayOn: Display
  88.         at: (label offset + (self displayTransformation applyTo: center) - center) rounded
  89.         clippingBox: aRectangle
  90.         rule: ruleInteger
  91.         mask: maskInteger!
  92.  
  93. displayView
  94.     "Interrogate the model and display the receiver accordingly."
  95.  
  96.     | box0 box1 box2 state |
  97.     box0 _ self insetDisplayBox.
  98.     box1 _ box0 insetBy: 1.
  99.     box2 _ box1 insetBy: 1.
  100.     Display white: box0.
  101.     state _ self interrogateModel.        "#white, #grey or #black"
  102.     state == #grey ifTrue: [Display veryLightGray: box0].
  103.     state == #black ifTrue: [Display black: box1].
  104.     emphasisOn 
  105.         ifFalse: [
  106.             Display white: box2.
  107.             self displayLabelClippingBox: box2 rule: Form under mask: Form black]
  108.         ifTrue: [
  109.             state == #black 
  110.                 ifTrue: [self displayLabelClippingBox: box2 rule: Form erase mask: Form black]
  111.                 ifFalse: [self displayLabelClippingBox: box2 rule: Form under mask: Form black]]! !
  112.  
  113. !TriStateButtonView methodsFor: 'initialize-release'!
  114.  
  115. initialize
  116.     super initialize.
  117.     selector _ #white! !
  118.  
  119. !TriStateButtonView methodsFor: 'selector'!
  120.  
  121. interrogateModel
  122.     "Send the selector message to the model to obtain the state of the aspect of the model 
  123.     the receiver indicates. Answer the result (a Symbol: #black, #grey or #white)."
  124.  
  125.     ^model perform: selector! !
  126.  
  127. !TriStateButtonView methodsFor: 'updating'!
  128.  
  129. update: aspectSymbol
  130.     "The model has changed in aspect 'aspectSymbol'. If this is the receiver's aspect, redisplay."
  131.  
  132.     aspectSymbol == selector ifTrue: [self display]! !
  133.  
  134. TriStateButtonView class
  135.     instanceVariableNames: ''!
  136.  
  137. !TriStateButtonView class methodsFor: 'instance creation'!
  138.  
  139. on: model aspect: aspectSymbol label: labelString click: clickSymbol press: pressSymbol
  140.     "Create a pluggable tri-state button on an aspect of model, where:
  141.         aspectSymbol    is the selector sent to the model to get the current status which must
  142.                         be #black, #grey or #white;
  143.         label            is the label on the button;
  144.         clickSymbol        is the selector sent to the model when the user clicks the button;
  145.         pressSymbol    is the selector sent to the model when the user presses the button
  146.                         and holds it for a predefined time."
  147.  
  148.     | view |
  149.     (view _ self new)
  150.         model: model;
  151.         selector: aspectSymbol;
  152.         label: labelString asParagraph.
  153.     view controller
  154.         clickSelector: clickSymbol;
  155.         pressSelector: pressSymbol.
  156.     ^view! !
  157.  
  158. !TriStateButtonView class methodsFor: 'examples'!
  159.  
  160. bothSwitches
  161.  
  162.     TriStateButtonExample bothSwitches!
  163.  
  164. stateSwitch
  165.  
  166.     TriStateButtonExample stateSwitch!
  167.  
  168. thisAndThatSwitch
  169.  
  170.     TriStateButtonExample thisAndThatSwitch! !
  171.  
  172. SwitchController subclass: #TriStateButtonController
  173.     instanceVariableNames: 'pressSelector downTime pressed '
  174.     classVariableNames: ''
  175.     poolDictionaries: ''
  176.     category: 'Interface-VisualControls'!
  177.  
  178. TriStateButtonController comment:
  179. 'I work with TriStateButtonView. I sense two kinds of events, a click and a prolonged press, 
  180. and signal my model accordingly. The minimum duration of a press is defined by my class method
  181. pressTime.
  182.  
  183. Instance Variables:
  184.  
  185. selector        The selector I send for a click. A Symbol. This variable is defined in my superclass.
  186. pressSelector    The selector I send for a press (button held for a preset time).
  187. downTime        The absolute time (from the millisecond clock) when the mouse button was pressed.
  188. pressed            A Boolean indicating that a press event has occurred (button down timed out).
  189.  
  190.                         Copyright (C) 1990, Ross P. Morley. 
  191.     This program is placed in the public domain. You may use and alter this program freely
  192.     for non-commercial purposes as long as you leave this message intact.  Neither I nor
  193.     my company will recognize any responsibility for damages arising from use of this program.
  194. '!
  195.  
  196. !TriStateButtonController methodsFor: 'accessing'!
  197.  
  198. clickSelector
  199.     "Answer the selector the receiver sends to its model when the user clicks on it 
  200.     (a Symbol, or nil if none)."
  201.  
  202.     ^selector!
  203.  
  204. clickSelector: aSymbolOrNil
  205.     "Set the selector the receiver sends to its model when the clicks on it. 
  206.     If nil, nothing is sent."
  207.  
  208.     selector _ aSymbolOrNil!
  209.  
  210. downTime
  211.     "Recall the absolute time (from the millisecond clock) when the red button was pressed."
  212.  
  213.     ^downTime!
  214.  
  215. pressSelector
  216.     "Answer the selector the receiver sends to its model when the user presses it 
  217.     for at least a preset length of time (a Symbol, or nil if none)."
  218.  
  219.     ^pressSelector!
  220.  
  221. pressSelector: aSymbolOrNil
  222.     "Set the selector the receiver sends to its model when the user presses it for. 
  223.     a preset length of time. If nil, nothing is sent."
  224.  
  225.     pressSelector _ aSymbolOrNil! !
  226.  
  227. !TriStateButtonController methodsFor: 'basic control sequence'!
  228.  
  229. controlInitialize
  230.     "The red button has gone down on the receiver. Begin timing it."
  231.  
  232.     view indicatorReverse.
  233.     self markDownTime;
  234.         clearPressed!
  235.  
  236. controlTerminate
  237.     "The red button has been released. If not pressed send a click event. Relinquish control."
  238.  
  239.     self hasBeenPressed ifTrue: [^self].
  240.     view indicatorReverse.
  241.     self sendClick! !
  242.  
  243. !TriStateButtonController methodsFor: 'control defaults'!
  244.  
  245. controlActivity
  246.     "Called repeatedly as long as a button is held. Check how long the button has been down
  247.     and, if long enough, generate a press event. After a press event has been generated, do
  248.     nothing (control is retained until the button is released)."
  249.  
  250.     self hasBeenPressed ifTrue: [^self].
  251.     Time millisecondClockValue - self downTime > self class pressTime 
  252.         ifTrue: [
  253.             view indicatorReverse.
  254.             self setPressed;
  255.                 sendPress]!
  256.  
  257. isControlActive
  258.     "Answer whether the receiver still wants control. True as long as a mouse button is held."
  259.  
  260.     ^sensor anyButtonPressed!
  261.  
  262. isControlWanted
  263.     "Answer whether the receiver initially wants control. Override the superclass to avoid 
  264.     setting the cursor because it is never restored (SwitchController bug)."
  265.  
  266.     ^self viewHasCursor & sensor redButtonPressed! !
  267.  
  268. !TriStateButtonController methodsFor: 'initialize-release'!
  269.  
  270. initialize
  271.     super initialize.
  272.     self clickSelector: #click; 
  273.         pressSelector: #press;
  274.         markDownTime;
  275.         clearPressed! !
  276.  
  277. !TriStateButtonController methodsFor: 'private'!
  278.  
  279. clearPressed
  280.  
  281.     pressed _ false!
  282.  
  283. hasBeenPressed
  284.  
  285.     ^pressed!
  286.  
  287. markDownTime
  288.     "Record the absolute time from the millisecond clock. Done when the red button is pressed."
  289.  
  290.     downTime _ Time millisecondClockValue!
  291.  
  292. sendClick
  293.     "Send a click event to the model, if it wants it."
  294.  
  295.     self clickSelector notNil
  296.         ifTrue: [model perform: self clickSelector]!
  297.  
  298. sendPress
  299.     "Send a press event to the model, if it wants it."
  300.  
  301.     self pressSelector notNil
  302.         ifTrue: [model perform: self pressSelector]!
  303.  
  304. setPressed
  305.  
  306.     pressed _ true! !
  307.  
  308. TriStateButtonController class
  309.     instanceVariableNames: ''!
  310.  
  311. !TriStateButtonController class methodsFor: 'defaults'!
  312.  
  313. pressTime
  314.     "Answer the length of time (ms) the button must be held down to be considered pressed.
  315.     If released before this time it is considered to have been clicked. An Integer."
  316.  
  317.     ^1200! !
  318.  
  319. Model subclass: #TriStateButtonExample
  320.     instanceVariableNames: 'state thisIs thatIs '
  321.     classVariableNames: ''
  322.     poolDictionaries: ''
  323.     category: 'Interface-VisualControls'!
  324.  
  325. TriStateButtonExample comment:
  326. 'This class exists to demonstrate TriStateButtons. It is not intended to be used itself in an
  327. application, but merely to show how to interface a TriStateButtonView/Controller to an
  328. application. It actually contains interfaces to two independent aspects which can be 
  329. indicated and controlled by tri-state buttons. Only the "interface" instance protocols
  330. need to be provided in your application class. The example class methods can be executed
  331. to show tri-state buttons in action.
  332.  
  333. Instance Variables:
  334.  
  335. state        A Symbol (#yes, #no or #maybe).
  336. thisIs        A Boolean.
  337. thatIs        A Boolean.'!
  338.  
  339. !TriStateButtonExample methodsFor: 'accessing'!
  340.  
  341. state
  342.     "Answer the current state (a Symbol: #yes, #no or #maybe)."
  343.  
  344.     ^state!
  345.  
  346. state: aSymbol
  347.     "Set the current state (#yes, #no or #maybe)."
  348.  
  349.     state _ aSymbol!
  350.  
  351. thatIs
  352.     "Answer whether that is true."
  353.  
  354.     ^thatIs!
  355.  
  356. thatIs: aBoolean
  357.  
  358.     thatIs _ aBoolean!
  359.  
  360. thisIs
  361.     "Answer whether this is true."
  362.  
  363.     ^thisIs!
  364.  
  365. thisIs: aBoolean
  366.  
  367.     thisIs _ aBoolean! !
  368.  
  369. !TriStateButtonExample methodsFor: 'initialize-release'!
  370.  
  371. initialize
  372.  
  373.     state _ #maybe.
  374.     thisIs _ thatIs _ false! !
  375.  
  376. !TriStateButtonExample methodsFor: 'interface - state'!
  377.  
  378. stateSwitchClick
  379.      "The 'state' switch has been clicked. Respond to it and notify the TriStateSwitchView
  380.     (a dependent) of the change. Click toggles between yes and no states, but has no effect
  381.     in a maybe state."
  382.  
  383.     self state == #maybe ifTrue: [^self].
  384.     self state == #yes 
  385.         ifTrue: [self state: #no]
  386.         ifFalse: [self state: #yes].
  387.     self changed: #stateSwitchState!
  388.  
  389. stateSwitchPress
  390.      "The 'state' switch has been pressed. Respond to it and notify the TriStateSwitchView
  391.     (a dependent) of the change. Press changes to the maybe state from yes or no, or to the
  392.     no state from maybe (spoils the symmetry, but better have some way out of maybe!!)."
  393.  
  394.     self state == #maybe 
  395.         ifTrue: [self state: #no]
  396.         ifFalse: [self state: #maybe].
  397.     self changed: #stateSwitchState!
  398.  
  399. stateSwitchState
  400.      "Answer the state the tri-state switch should be in for the present state 
  401.     (a Symbol: #black, #grey or #white)."
  402.  
  403.     self state == #yes ifTrue: [^#black].
  404.     self state == #no ifTrue: [^#white].
  405.     ^#grey! !
  406.  
  407. !TriStateButtonExample methodsFor: 'interface - this & that'!
  408.  
  409. thisAndThatSwitchClick
  410.     "The 'this & that' switch has been clicked. If this is then toggle that, otherwise that is 
  411.     irrelevant so remember its previous state and (as a shortcut) set this. 
  412.     Notify the TriStateSwitchView (a dependent) of the change."
  413.  
  414.     self thisIs 
  415.         ifTrue: [self thatIs: self thatIs not]
  416.         ifFalse: [self thisIs: true].
  417.     self changed: #thisAndThatSwitchState!
  418.  
  419. thisAndThatSwitchPress
  420.     "The 'this & that' switch has been pressed. Toggle this. 
  421.     Notify the TriStateSwitchView (a dependent) of the change."
  422.  
  423.     self 
  424.         thisIs: self thisIs not;
  425.         changed: #thisAndThatSwitchState!
  426.  
  427. thisAndThatSwitchState
  428.     "Answer the state the tri-state switch should be in according to this and that
  429.     (a Symbol: #black, #grey or #white)."
  430.  
  431.     self thisIs
  432.         ifTrue: [self thatIs 
  433.                     ifTrue: [^#black]     "this is and that is"
  434.                     ifFalse: [^#grey]    "this is but that isn't"
  435.                 ]
  436.         ifFalse: [^#white]                "this isn't and don't care about that"! !
  437.  
  438. TriStateButtonExample class
  439.     instanceVariableNames: ''!
  440.  
  441. !TriStateButtonExample class methodsFor: 'instance creation'!
  442.  
  443. new
  444.  
  445.     ^super new initialize! !
  446.  
  447. !TriStateButtonExample class methodsFor: 'examples'!
  448.  
  449. bothSwitches
  450.     "Open a window with two TriStateSwitchViews."
  451.     "TriStateButtonExample bothSwitches"
  452.  
  453.     | model sSwitch ttSwitch topView |
  454.     model _ self new.
  455.     sSwitch _ TriStateButtonView on: model
  456.             aspect: #stateSwitchState 
  457.             label: 'State' 
  458.             click: #stateSwitchClick
  459.             press: #stateSwitchPress.
  460.     ttSwitch _ TriStateButtonView on: model
  461.             aspect: #thisAndThatSwitchState 
  462.             label: 'This & That' 
  463.             click: #thisAndThatSwitchClick
  464.             press: #thisAndThatSwitchPress.
  465.     (topView _ StandardSystemView new)
  466.         model: model;
  467.         minimumSize: 100@80;
  468.         borderWidth: 1;
  469.         addSubView: sSwitch in: (0@0 corner: 1.0@0.5) borderWidth: 1;
  470.         addSubView: ttSwitch in: (0@0.5 corner: 1.0@1.0) borderWidth: 1.
  471.     topView controller open.!
  472.  
  473. stateSwitch
  474.     "Open a window with a TriStateSwitchView on my state."
  475.     "TriStateButtonExample stateSwitch"
  476.  
  477.     | model switch topView |
  478.     model _ self new.
  479.     switch _ TriStateButtonView on: model
  480.             aspect: #stateSwitchState 
  481.             label: 'State' 
  482.             click: #stateSwitchClick
  483.             press: #stateSwitchPress.
  484.     (topView _ StandardSystemView new)
  485.         model: model;
  486.         minimumSize: 100@50;
  487.         borderWidth: 1;
  488.         addSubView: switch.
  489.     topView controller open.!
  490.  
  491. thisAndThatSwitch
  492.     "Open a window with a TriStateSwitchView on my this and that."
  493.     "TriStateButtonExample thisAndThatSwitch"
  494.  
  495.     | model switch topView |
  496.     model _ self new.
  497.     switch _ TriStateButtonView on: model
  498.             aspect: #thisAndThatSwitchState 
  499.             label: 'This & That' 
  500.             click: #thisAndThatSwitchClick
  501.             press: #thisAndThatSwitchPress.
  502.     (topView _ StandardSystemView new)
  503.         model: model;
  504.         minimumSize: 100@50;
  505.         borderWidth: 1;
  506.         addSubView: switch.
  507.     topView controller open.! !
  508.  
  509.